package moon.excel.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import moon.util.StringUtils;

/***
 * 单字符表达式解析，每个表达式中的各个关键字只能是一个字符
 * 
 * @author Sunset
 * 
 */
public class SimpleMonocaseELParse {
	private Set<EL> registeEls = new LinkedHashSet<EL>();
	private Map<Character, List<EL>> startElsMap = new HashMap<Character, List<EL>>();

	public List<Map.Entry<String, String>> parse(String desStr) {
		List<Map.Entry<String, String>> parseResultEls = new ArrayList<Map.Entry<String, String>>();
		List<EL> ingEls = new ArrayList<EL>();
		boolean isElStart = false;
		int start = 0;
		if (StringUtils.isNotBlank(desStr)) {
			for (int i = 0; i < desStr.length(); i++) {
				char c = desStr.charAt(i);
				if (!isElStart && ingEls.isEmpty()) {
					ingEls = getAvailableEl(c);
					if (!ingEls.isEmpty()) {
						if (i > start) {
							parseResultEls.add(new MyEntry<String, String>("", desStr.substring(start, i)));
						}
						start = i;
						isElStart = true;
					}
				}
				if (isElStart) {
					MyEntry<EL, Integer> entry = parseEL(i, ingEls, desStr);
					parseResultEls.add(new MyEntry<String, String>(entry.key.getName(), desStr.substring(start, entry.value + 1)));
					i = entry.value;
					start = i + 1;
					isElStart = false;
					ingEls.clear();
				} else if (i == desStr.length() - 1 && i + 1 > start) {
					parseResultEls.add(new MyEntry<String, String>("", desStr.substring(start, i + 1)));
				}

			}
		}
		return parseResultEls;
	}

	/***
	 * 平衡策略，（处理内嵌表达式）解析表达式。
	 * 
	 * @param start
	 * @param desStr
	 * @return <结束的表达式,结束索引>
	 */
	private MyEntry<EL, Integer> parseEL(int start, List<EL> desEls, String desStr) {
		for (int i = start + 1; i < desStr.length(); i++) {
			char c = desStr.charAt(i);
			List<EL> temps = getAvailableEl(c);
			if (!temps.isEmpty()) {
				MyEntry<EL, Integer> entry = parseEL(i, temps, desStr);
				i = entry.value;
			} else {
				EL el = isEnd(desEls, c);
				if (el != null) {
					if (!el.match(desStr.substring(start, i + 1))) {
						throw new RuntimeException("字符串中存在无效表达式");
					}
					return new MyEntry<EL, Integer>(el, i);
				}
			}
		}
		throw new RuntimeException("字符串中存在无效表达式");
	}

	/**
	 * 得到指定开始字符的表达式
	 * 
	 * @param desc
	 * @return
	 */
	private List<EL> getAvailableEl(char desc) {
		List<EL> list = new ArrayList<EL>();
		if (startElsMap.containsKey(desc)) {
			list = new ArrayList<SimpleMonocaseELParse.EL>(startElsMap.get(desc));
		}
		return list;
	}

	/**
	 * 返回结束表达式
	 * 
	 * @param els
	 * @param c
	 * @return
	 */
	private EL isEnd(List<EL> els, char c) {
		for (EL el : els) {
			if (el.getEndChar() == c) {
				return el;
			}
		}
		return null;
	}

	/**
	 * 注册表达式
	 * 
	 * @param name
	 *            名称
	 * @param start
	 *            开始字符
	 * @param end
	 *            结束字符
	 * @return
	 */
	public boolean register(String name, char start, char end) {
		return register(name, start, end, null);
	}

	/**
	 * 注册表达式
	 * 
	 * @param name
	 * @param start
	 * @param end
	 * @param pattern
	 *            验证表达式
	 * @return
	 */
	public boolean register(String name, char start, char end, Pattern pattern) {
		boolean r = false;
		if (StringUtils.isBlank(name)) {
			throw new RuntimeException("表达式名称不能为空且表达式至少包含两个字符");
		}
		EL el = new EL(name, start, end, pattern);
		if (StringUtils.isNotBlank(name) && registeEls.add(el)) {
			List<EL> list = startElsMap.get(el.getStartChar());
			if (list == null) {
				list = new ArrayList<EL>();
				startElsMap.put(el.getStartChar(), list);
			}
			list.add(el);
			r = true;
		}
		return r;
	}

	private static class EL {
		private String name;
		private char startChar;
		private char endChar;
		private Pattern pattern;

		public EL(String name, char startChar, char endChar, Pattern pattern) {
			super();
			this.name = name;
			this.startChar = startChar;
			this.endChar = endChar;
			this.pattern = pattern;
		}

		public String getName() {
			return name;
		}

		public char getStartChar() {
			return startChar;
		}

		public char getEndChar() {
			return endChar;
		}

		public boolean match(String desc) {
			return pattern == null || pattern.matcher(desc).matches();
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + endChar;
			result = prime * result + startChar;
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			EL other = (EL) obj;
			if (endChar != other.endChar)
				return false;
			if (startChar != other.startChar)
				return false;
			return true;
		}

	}

	static class MyEntry<K, V> implements Map.Entry<K, V> {
		private K key;
		private V value;

		public MyEntry(K key, V value) {
			super();
			this.key = key;
			this.value = value;
		}

		@Override
		public K getKey() {
			// TODO Auto-generated method stub
			return this.key;
		}

		@Override
		public V getValue() {
			// TODO Auto-generated method stub
			return this.value;
		}

		@Override
		public V setValue(V value) {
			// TODO Auto-generated method stub
			return this.value = value;
		}

		public void SetKey(K key) {
			this.key = key;
		}

		@Override
		public String toString() {
			return "MyEntry [key=" + key + ", value=" + value + "]";
		}

	}

	public static void main(String[] args) {
		SimpleMonocaseELParse elParse = new SimpleMonocaseELParse();
		elParse.register("text", '{', '}');
		elParse.register("fun", '$', ')');
		System.out.println(Arrays.toString(elParse.parse("cde{fgh}$i.j(sdf)").toArray()));
	}
}
